/* bzflag
 * Copyright (c) 1993-2020 Tim Riker
 *
 * This package is free software;  you can redistribute it and/or
 * modify it under the terms of the license found in the file
 * named COPYING that should have accompanied this file.
 *
 * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 */

// bzflag common header
#include "common.h"

// interface header
#include "Address.h"

// system headers
#include <string.h>
#include <string>
#include <sys/types.h>
#if !defined(_WIN32)
#include <unistd.h>
#include <setjmp.h>
#endif

// common implementation headers
#include "bzsignal.h"
#include "ErrorHandler.h"

// local implementation headers
#include "Pack.h"

#if defined(sun)
// Solaris...
extern "C" int inet_aton(const char *, struct in_addr *);
#endif

// RFC 1035 limits the length of a fully qualified domain name to 255
// octets, so calling sysconf(_SC_HOST_NAME_MAX) to find some other
// limit and/or dynamically expanding the gethostname() buffer on
// demand are complex solutions to a problem that does not exist in
// the environments for which BZFlag is designed.  The value chosen
// here provides a substantial margin in case the RFC 1035 limit is
// raised in the future.
#ifndef MAXHOSTNAMELEN
#define MAXHOSTNAMELEN  511
#endif

//
// Address
//

Address::Address()
{
    InAddr tempAddr;

    memset(&tempAddr, 0, sizeof(tempAddr));
    tempAddr.s_addr = htonl(INADDR_ANY);
    addr.push_back(tempAddr);
}

Address::Address(const std::string& name)
{
    Address a = getHostAddress(name);
    addr.push_back(a.addr[0]);
}

Address::Address(const Address& address) : addr(address.addr)
{
    // do nothing
}

Address::Address(const InAddr& _addr)
{
    addr.push_back(_addr);
}

Address::Address(const struct sockaddr_in& _addr)
{
    addr.push_back(_addr.sin_addr);
}

Address::~Address()
{
    // do nothing
}

Address&        Address::operator=(const Address& address)
{
    addr.clear();
    addr.push_back(address.addr[0]);
    return *this;
}

Address::operator InAddr() const
{
    return addr[0];
}

bool            Address::operator==(const Address& address) const
{
    return addr[0].s_addr == address.addr[0].s_addr;
}

bool            Address::operator!=(const Address& address) const
{
    return addr[0].s_addr != address.addr[0].s_addr;
}

bool            Address::operator<(Address const& address) const
{
    return addr[0].s_addr < address.addr[0].s_addr;
}
bool            Address::isAny() const
{
    return addr[0].s_addr == htonl(INADDR_ANY);
}

bool            Address::isPrivate() const
{
    // 127.0.0.0/8
    if ((addr[0].s_addr & htonl(0xff000000u)) == htonl(0x7f000000u))
        return(true);
    // 10.0.0.0/8
    if ((addr[0].s_addr & htonl(0xff000000u)) == htonl(0x0a000000u))
        return(true);
    // 172.16.0.0/12
    if ((addr[0].s_addr & htonl(0xfff00000u)) == htonl(0xac100000u))
        return(true);
    // 192.168.0.0/16
    if ((addr[0].s_addr & htonl(0xffff0000u)) == htonl(0xc0a80000u))
        return(true);
    return(false);
}

std::string     Address::getDotNotation() const
{
    return std::string(inet_ntoa(addr[0]));
}

uint8_t         Address::getIPVersion() const
{
    return 4;
}

static const struct hostent* bz_gethostbyname(const std::string &name)
{
    const struct hostent* hent = NULL;

    if (name.length() > 0)
        hent = gethostbyname(name.c_str());
    else
    {
        // use our own host name if none is specified
        char hostname[MAXHOSTNAMELEN+1];
        if (gethostname(hostname, MAXHOSTNAMELEN) >= 0)
        {
            // ensure null termination regardless of
            // gethostname() implementation details
            hostname[MAXHOSTNAMELEN] = '\0';
            hent = gethostbyname(hostname);
        }
    }
    return hent;
}

Address Address::getHostAddress(const std::string &hname)
{
    Address a;
    InAddr tempAddr;
    int j;

    if (hname.length() > 0 && inet_aton(hname.c_str(), &tempAddr) != 0)
    {
        a.addr.clear();
        a.addr.push_back(tempAddr);
        return a;
    }

    const struct hostent* hent = bz_gethostbyname(hname);
    if (!hent)
    {
        herror("Looking up host name");
        return a;
    }

    a.addr.clear();
    for (j=0; hent->h_addr_list[j] != NULL; j++)
    {
        ::memcpy(&tempAddr, hent->h_addr_list[j], sizeof(tempAddr));
        a.addr.push_back(tempAddr);
    }
    return a;
}

std::string     Address::getHostByAddress(InAddr addr)
{
    int addrLen = sizeof(addr);
    struct hostent* hent = gethostbyaddr((char*)&addr, addrLen, AF_INET);

    if (!hent)
    {
        // can't lookup name -- return in standard dot notation
        return std::string(inet_ntoa(addr));
    }
    return std::string(hent->h_name);
}

const std::string Address::getHostName(const std::string &hostname) // const
{
    const struct hostent* hent = bz_gethostbyname(hostname);
    if (!hent)
        return std::string();
    return std::string(hent->h_name);
}

void*           Address::pack(void* _buf) const
{
    unsigned char* buf = (unsigned char*)_buf;
    buf = (unsigned char*)nboPackUByte(_buf, 4);
    // everything in InAddr  is already in network byte order
    int32_t hostaddr = int32_t(addr[0].s_addr);
    ::memcpy(buf, &hostaddr, sizeof(int32_t));
    buf += sizeof(int32_t);
    return (void*)buf;
}

const void*     Address::unpack(const void* _buf)
{
    const unsigned char* buf = (const unsigned char*)_buf;
    InAddr tempAddr;
    // FIXME - should actually parse the first byte to see if it's IPv4 or
    // IPv6
    ++buf;
    // everything in InAddr should be stored in network byte order
    int32_t hostaddr;
    ::memcpy(&hostaddr, buf, sizeof(int32_t));
    buf += sizeof(int32_t);
    tempAddr.s_addr = u_long(hostaddr);
    addr.clear();
    addr.push_back(tempAddr);
    return buf;
}

//
// ServerId
//

void*           ServerId::pack(void* _buf) const
{
    // everything in ServerId is already in network byte order
    unsigned char* buf = (unsigned char*)_buf;
    int32_t hostaddr = int32_t(serverHost.s_addr);
    ::memcpy(buf, &hostaddr, sizeof(int32_t));
    buf += sizeof(int32_t);
    ::memcpy(buf, &port, sizeof(int16_t));
    buf += sizeof(int16_t);
    ::memcpy(buf, &number, sizeof(int16_t));
    buf += sizeof(int16_t);
    return (void*)buf;
}

const void*     ServerId::unpack(const void* _buf)
{
    // everything in ServerId should be stored in network byte order
    const unsigned char* buf = (const unsigned char*)_buf;
    int32_t hostaddr;
    ::memcpy(&hostaddr, buf, sizeof(int32_t));
    buf += sizeof(int32_t);
    ::memcpy(&port, buf, sizeof(int16_t));
    buf += sizeof(int16_t);
    ::memcpy(&number, buf, sizeof(int16_t));
    buf += sizeof(int16_t);
    serverHost.s_addr = u_long(hostaddr);
    return buf;
}

bool            ServerId::operator==(const ServerId& id) const
{
    return serverHost.s_addr == id.serverHost.s_addr &&
           port == id.port && number == id.number;
}

bool            ServerId::operator!=(const ServerId& id) const
{
    return serverHost.s_addr != id.serverHost.s_addr ||
           port != id.port || number != id.number;
}

// Local Variables: ***
// mode: C++ ***
// tab-width: 4 ***
// c-basic-offset: 4 ***
// indent-tabs-mode: nil ***
// End: ***
// ex: shiftwidth=4 tabstop=4
